package com.gopay.common.trace.helper;

import com.gopay.common.constants.txncd.MerTxnCd;
import com.gopay.common.trace.LogConst;
import com.gopay.common.trace.TraceLog;
import com.gopay.common.trace.TraceLogUtils;
import com.gopay.common.trace.def.BinaryAnnotation;
import com.gopay.common.trace.def.Endpoint;
import com.gopay.common.trace.def.Span;
import com.gopay.common.trace.def.Tracer;
import com.gopay.common.trace.enums.TraceType;
import com.gopay.common.util.CommonUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ddh02
 * @LogSpringMvcHelper
 * @2017/5/19 11:37  www.gopay.com.cn Inc.All rights reserved.
 */


public class LogSpringMvcHelper extends LogAbstractHelper {


    private static List<String> includeMerTxnCd = new ArrayList<>();


    static {
        includeMerTxnCd.add(MerTxnCd._8888.value);
        includeMerTxnCd.add(MerTxnCd._8801.value);
        includeMerTxnCd.add(MerTxnCd._8802.value);
        includeMerTxnCd.add(MerTxnCd._8803.value);
        includeMerTxnCd.add(MerTxnCd._4025.value);
        includeMerTxnCd.add(MerTxnCd._8805.value);
        includeMerTxnCd.add(MerTxnCd._SC01.value);
        includeMerTxnCd.add(MerTxnCd._SC02.value);
        includeMerTxnCd.add(MerTxnCd._SC03.value);
        /*includeMerTxnCd.add(MerTxnCd._DS01.value);
        includeMerTxnCd.add(MerTxnCd._DS02.value);
        includeMerTxnCd.add(MerTxnCd._DS03.value);
        includeMerTxnCd.add(MerTxnCd._DS04.value);
        includeMerTxnCd.add(MerTxnCd._DS05.value);
        includeMerTxnCd.add(MerTxnCd._4010.value);
        includeMerTxnCd.add(MerTxnCd._4012.value);
        includeMerTxnCd.add(MerTxnCd._ZDS1.value);*/
    }

    @Override
    public void before(Object... args) {

        StringBuffer remark = new StringBuffer(); //span中存储的 备注信息

        HttpServletRequest request = (HttpServletRequest) args[0];

        //校验该请求是否过滤
        if (!checkGateway(request, remark)) {
            return ;
        }
        //response.setHeader("Access-Control-Allow-Origin", "*");

        Integer count = logContext.incrCount(); //计数加1

        String url = request.getRequestURL().toString();

        String tid = request.getParameter(LogConst.TID);

        String orderId = request.getParameter(LogConst.ORDER_ID);

        //附加信息
        //remark.append(StringUtils.isBlank(orderId) ? "" : orderId + "|");

        String pid = null;

        Endpoint ep = getEP(request);

        Span spanNew = null;

        try {


            String t_id = (String) request.getHeader(LogConst.TID);
            String spanid = (String) request.getHeader(LogConst.SPANID);


            if (StringUtils.isNotBlank(t_id) && StringUtils.isNotBlank(spanid)) {
                //都存在  说明是从 httpclient 传过来的
                tid = t_id;
                pid = spanid;
            }

            //如果为空  就看 session中 是否存在
            if (StringUtils.isBlank(tid)) {
                //既不是重定向来的 也不是 httpclient过来的  则尝试从session中取
                tid = (String) request.getSession().getAttribute(LogConst.TID);
            }

            // 这里获取父span 根据当前计数减一 去获取
            Span sp1 = logContext.getSpan(count - 1);

            if (sp1 == null) {
                //当前线程没有 父span  说明自己是第一个  即不是 forward 过来的

                //当没有 父span的时候 说明不是 forward过来的 是首次 那么就可以先清空 所有的本地资源 然后重新计数
                logContext.clearThreadLocal();
                count = logContext.incrCount();//重新计数

                if (StringUtils.isBlank(tid)) {
                    //说明也没有 tid传递   构建rootspan
                    spanNew = tracer.newSpan(request.getRequestURI(), TraceType.SPRING_MVC);

                } else {
                    //有tid 说明它是下游的  redirect过来的 ******pid*******没传过来 httpclient过来的有pid
                    spanNew = tracer.genSpan(tid, pid, request.getRequestURI(), TraceType.SPRING_MVC);
                }

            } else {
                //当前线程里 有span对象 说明是内部forward了
                spanNew = tracer.genSpan(sp1.getTid(), sp1.getId(), request.getRequestURI(), TraceType.SPRING_MVC);

                sp1.addChild(spanNew); //加入到父中
            }

            spanNew.setOid(orderId);
           // addOrderAnnotation(ep, remark.toString(), spanNew);
            logContext.setTid(spanNew.getTid());
            logContext.setSpan(count, spanNew);

            // 收到请求 为 sr
            tracer.serverReceive(spanNew, ep, startTime);  //入 本地线程

        } catch (Exception e) {
            printSelfException(e, "springmvc before 阶段自身异常！");
        } finally {
        }
    }

    @Override
    public void middle(Object... args) {

        HttpServletRequest request = (HttpServletRequest) args[0];
        ModelAndView modelAndView = (ModelAndView) args[1];
        String useSessionMark = "1";
        if(args[2] != null){
            useSessionMark = (String)args[2];
        }


        if (!(boolean) request.getAttribute("_isFilter_")) {
            return;
        }

        try {
            Integer count = logContext.getCount();
            Span now = logContext.getSpan(count);

            if (now == null) {
                return;
            }

            String tid = now.getTid();

            if (StringUtils.isNotBlank(tid)) {

                //处理redirect 跳转 带参数情况
                if (modelAndView != null &&
                        modelAndView.getViewName() != null
                        && modelAndView.getViewName().startsWith("redirect:")) {

                    if (modelAndView.getViewName().indexOf("?") != -1) {
                        modelAndView.setViewName(modelAndView.getViewName() + "&" + LogConst.TID + "=" + tid);
                    } else {
                        modelAndView.setViewName(modelAndView.getViewName() + "?" + LogConst.TID + "=" + tid);
                    }
                } else if (modelAndView != null
                        && modelAndView.getView() != null
                        && modelAndView.getView() instanceof RedirectView) {

                    String url = ((RedirectView) modelAndView.getView()).getUrl();
                    if (url.indexOf("?") != -1) {
                        url = url + "&" + LogConst.TID + "=" + tid;
                    } else {
                        url = url + "?" + LogConst.TID + "=" + tid;
                    }
                    ((RedirectView) modelAndView.getView()).setUrl(url);
                } else if (modelAndView != null &&
                        modelAndView.getViewName() != null
                        && modelAndView.getViewName().startsWith("forward:")) {
                    //不做处理
                } else {
                    // logger.info("==================================" + tid);
                    //request.setAttribute(LogConst.TID, tid);
                    // CookieUtil.addCookie(response, LogConst.TID, tid, 1800);
                    //modified by zyt,2017-12-28,根据useSessionMark判断是否需要将tid放入session
                    if(StringUtils.equals(useSessionMark, LogConst.PUT_TID_IN_SESSION)) {
                        request.getSession().setAttribute(LogConst.TID, tid);
                    }

                }
            }
           // Endpoint ep = getEP(request);
          //  long end = System.currentTimeMillis();
            //这里是cs
          //  tracer.clientSend(now, ep, end);
           //======== now.setMiddleTime(System.currentTimeMillis());
        } catch (Exception e) {
            //TraceLogUtils.log(new TraceLog(TraceType.SPRING_MVC, 0, e));
            printSelfException(e, "springmvc middle 阶段自身异常！");
        } finally {

        }

    }

    @Override
    public void after(Object... args) {

        HttpServletRequest request = (HttpServletRequest) args[0];

        HttpServletResponse response = (HttpServletResponse) args[1];
        Exception ex = (Exception) args[2];

        if (!(boolean) request.getAttribute("_isFilter_")) {
            return;
        }
        Integer  count = logContext.dncrCount(); ///计数减一

        //after 之后 为 ss
        Span now = null;
        try {
            now = logContext.getSpan(count + 1);  //获取当前的 需要再加上1
            if (now == null) {
                return;
            }

            span = now;

            Endpoint ep = getEP(request);

            if (ex != null) {
                ex(ex);
                //return;  ///ex 只记录异常  因此这里不返回
            }

            long end = System.currentTimeMillis();
            tracer.serverSend(now, ep, end);

        } catch (Exception e) {
            printSelfException(e, "springmvc after 阶段自身异常！");
        } finally {
            tracer.endAndShow(now);
        }
    }

    @Override
    public void ex(Throwable throwable) {
        commonProcessEx(throwable);
    }

    @Override
    public TraceType getTraceType() {
        return TraceType.SPRING_MVC;
    }


    private boolean checkGateway(HttpServletRequest request, StringBuffer remark) {

            String url = request.getRequestURL().toString();
            request.setAttribute("_isFilter_", true);

            if (url.indexOf("gateway") != -1){

                //gateway  清除session里的tid
                request.getSession().removeAttribute(LogConst.TID);
                logContext.clearThreadLocal();

                // 针对gateway处理
                String tranCode = request.getParameter("tranCode");
               // String merOrderNum = request.getParameter("merOrderNum");
              //  remark.append(tranCode);

                if (StringUtils.isNotBlank(tranCode) && includeMerTxnCd.contains(tranCode)) {
                    return true;
                } else {

                    request.setAttribute("_isFilter_", false);
                    return false;
                }

            } else if (url.indexOf("bis") != -1){
                //String bank = request.getParameter("bank");
                //remark.append(StringUtils.isBlank(bank) ? "" : bank + "|");
            }
            return true;
    }

    private Endpoint getEP(HttpServletRequest request) {
        Endpoint ep = Tracer.getTracer().newEndPoint();
        ep.setIp(CommonUtils.getRemoteRealIpAddr(request));
        ep.setPort(request.getRemotePort());
        return ep;
    }

    /***
     * 给span加入其他信息
     * @param ep
     * @param msg
     * @param span
     */
    private void addOrderAnnotation(Endpoint ep, String msg, Span span) {

        if (StringUtils.isBlank(msg)) {
            return;
        }
        BinaryAnnotation binaryAnnotation = new BinaryAnnotation();
        //binaryAnnotation.setEndpoint(ep);
        binaryAnnotation.setType("order");
        binaryAnnotation.setKey("springmvc.order");
        binaryAnnotation.setValue(msg);
        span.addBinaryAnnotation(binaryAnnotation);

    }

}
